#pragma once
#include<elasticlient/client.h>
#include<cpr/cpr.h>
#include<json/json.h>
#include<iostream>
#include<memory>
#include"logger.hpp"

bool Serialize(const Json::Value& val,std::string& dst)
{
    //先定义Json::StreamWriter 工厂类 Json::StreamWriterBuilder
    Json::StreamWriterBuilder swb; 
    swb.settings_["emitUTF8"] = true;
    //通过Json::StreamWriter中的write接口进行序列化
    std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
    std::stringstream ss;
    int ret = sw->write(val,&ss);
    if(ret != 0)
    {
        std::cout<<"Json反序列化失败";
        return false;
    }
    dst = ss.str();
    return true;
}

bool UnSerialize(const std::string& src,Json::Value& val)
{
    //先定义Json::StreamWriter 工厂类 Json::StreamWriterBuilder
    Json::CharReaderBuilder crb; 
    crb.settings_["emitUTF8"] = true;
    //通过Json::StreamWriter中的write接口进行序列化
    std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
    std::string err;
    int ret = cr->parse(src.c_str(),src.c_str() + src.size(),&val,&err);
    if(ret == false)
    {
        std::cout<<"Json反序列化失败"<<err<<std::endl;
        return false;
    }
    return true;
}
class ESIndex
{
public:
    ESIndex(std::shared_ptr<elasticlient::Client>& client,const std::string& name\
        ,const std::string& type = "_doc")
        :_name(name)
        ,_type(type)
        ,_client(client)
    {
        Json::Value settings;
        Json::Value analysis;
        Json::Value analyzer;
        Json::Value ik;
        Json::Value tokenizer;
        tokenizer["tokenizer"] = "ik_max_word";
        ik["ik"] = tokenizer;
        analyzer["analyzer"] = ik;
        analysis["analysis"] = analyzer;
        _index["settings"] = analysis;

        std::string str;
        int ret = Serialize(settings,str);
    }

    void append(const std::string& key,const std::string& type = "text",\
        const std::string& analyzer = "ik_max_word",bool enabled = true)
    {
        Json::Value fields;
        fields["type"] = type;
        fields["analyzer"] = analyzer;
        if(enabled == false)
        {
            fields["enabled"] = enabled;
        }
        _properties[key] = fields;
    }
    
    bool create(const std::string& index_id = "default_index_id")
    {
        Json::Value mappings;
        mappings["dynamic"] = true;
        mappings["properties"] = _properties;
        _index["mappings"] = mappings;

        std::string body;
        bool ret = Serialize(_index,body);
        if(ret == false)
        {
            LOG_ERROR("序列化索引失败!");
            return false;
        }
        LOG_INFO("请求正文:{}",body);
        
        //发起搜索请求
        cpr::Response rsp;
        try{
            auto rsp = _client->index(_name,_type,index_id,body);
            //打印相应状态码和响应正文
            if(rsp.status_code <200 || rsp.status_code >=300)
            {
                LOG_ERROR("创建ES索引失败{},响应状态码异常{}",_name,rsp.status_code);
                return false;
            }
        }catch(std::exception& e){
           LOG_ERROR("创建ES索引失败{}-{}",_name,e.what());
            return false;
        }
        LOG_DEBUG("创建ES索引成功");
        return true;
    }
private:
    std::string _name;
    std::string _type;
    Json::Value _properties;
    Json::Value _index;
    std::shared_ptr<elasticlient::Client> _client;
};


class ESInsert
{
public:
    ESInsert(std::shared_ptr<elasticlient::Client>& client,const std::string& name,\
        const std::string& type = "_doc")
        :_name(name)
        ,_type(type)
        ,_client(client)
        {}
    template<typename T>
    void append(const std::string& key,const T& val)
    {
        _item[key] = val;
    }
    bool insert(const std::string& id ="")
    {
        std::string body;
        bool ret = Serialize(_item,body);
        if(ret == false)
        {
            LOG_ERROR("序列化索引失败!");
            return false;
        }
        LOG_DEBUG("{}",body);

        //发起搜索请求
        try{
            auto rsp = _client->index(_name,_type,id,body);
            //打印相应状态码和响应正文
            if(rsp.status_code <200 || rsp.status_code >=300)
            {
                LOG_ERROR("新增失败{},响应状态码异常{}",_name,rsp.status_code);
                return false;
            }
        }catch(std::exception& e){
           LOG_ERROR("新增失败{}-{}",body,e.what());
            return false;
        }
        return true;
    }
private:
    std::string _name;
    std::string _type;
    Json::Value _item;
    std::shared_ptr<elasticlient::Client> _client;
};

class ESRemove
{
public:
    ESRemove(std::shared_ptr<elasticlient::Client>& client,const std::string& name\
        ,const std::string& type = "_doc")
    :_name(name)
    ,_type(type)
    ,_client(client)
    {}
    bool remove(const std::string& id)
    {
        //发起搜索请求
        try{
            auto rsp = _client->remove(_name,_type,id);
            //打印相应状态码和响应正文
            if(rsp.status_code <200 || rsp.status_code >=300)
            {
                LOG_ERROR("删除失败{},响应状态码异常{}",id,rsp.status_code);
                return false;
            }
        }catch(std::exception& e){
            LOG_ERROR("删除失败{}-{}",id,e.what());
            return false;
        }
        return true;
    }
private:
    std::string _name;
    std::string _type;
    std::shared_ptr<elasticlient::Client> _client;
};


class ESsearch
{
public:
    ESsearch(std::shared_ptr<elasticlient::Client>& client,const std::string& name\
        ,const std::string& type = "_doc")
    :_name(name)
    ,_type(type)
    ,_client(client)
    {}
    void append_must_not_terms(const std::string& key,const std::vector<std::string>& vals)
    {
        Json::Value fields;
        for(const auto& val :vals)
        {
            fields[key].append(val);
        }
        Json::Value terms;
        terms["terms"] = fields;
        _must_not.append(terms);
    }
    void append_should_match(const std::string& key,const std::string& val)
    {
        Json::Value field;
        field[key] = val;
        Json::Value match;
        match["match"] = field;
        _should.append(match);
    }
    void append_must_term(const std::string& key,const std::string& val)
    {
        Json::Value field;
        field[key] = val;
        Json::Value term;
        term["term"] = field;
        _must.append(term);
    }
    void append_must_match(const std::string& key,const std::string& val)
    {
        Json::Value field;
        field[key] = val;
        Json::Value match;
        match["match"] = field;
        _must.append(match);
    }
    Json::Value search()
    {
        Json::Value cond;
        if(_must_not.empty() == false)
        {
            cond["must_not"] = _must_not;
        }
        if(_should.empty() == false)
        {
            cond["should"] = _should;
        }
        if(_must.empty() == false)
        {
            cond["must"] = _must;
        }
        Json::Value query;
        query["bool"] = cond;
        Json::Value root;
        root["query"] = query;

        std::string body;
        bool ret = Serialize(root,body);
        if(ret == false)
        {
            LOG_ERROR("序列化索引失败!");
            return Json::Value();
        }
        LOG_DEBUG("{}",body);
        //发起搜索请求
        cpr::Response rsp;
        try{
            rsp = _client->search(_name,_type,body);
            //打印相应状态码和响应正文
            if(rsp.status_code <200 || rsp.status_code >=300)
            {
                LOG_ERROR("检索数据失败{},响应状态码异常{}",_name,rsp.status_code);
                return Json::Value();
            }
        }catch(std::exception& e){
           LOG_ERROR("检索数据失败{}-{}",body,e.what());
            return Json::Value();
        }
        //对响应正文进行反序列化
        Json::Value json_res;
        ret = UnSerialize(rsp.text,json_res);
        if(ret == false)
        {
            LOG_ERROR("检索数据失败{},结果反序列化失败",rsp.text);
            return Json::Value();
        }
        return json_res["hits"]["hits"];
    }
private:
    std::string _name;
    std::string _type;
    Json::Value _must_not;
    Json::Value _should;
    Json::Value _must;
    std::shared_ptr<elasticlient::Client> _client;
};